/**
 * @Copyright ®2015 Sinosoft Co. Ltd. All rights reserved.<br/>
 * 项目名称 : 电子商务云服务平台
 * 创建日期 : 2016年6月28日
 * 修改历史 : 
 *     1. [2016年6月28日]创建文件 by Admin
 */
package com.gavinwind.inspay.common.util;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 数学计算工具类
 * @date 2010-11-23
 * @since JDK 1.5
 * @author dingkui
 * @version 1.0
 *
 */
public class MathUtil {

    /**
     * 默认除法精度
     */
    private static final int DEF_DIV_SCALE = 2;

    /**
     * 默认数字，一亿
     */
    private static final BigDecimal HUNDRED_MILLION = new BigDecimal("100000000");

    /**
     * 默认数字，一万
     */
    private static final BigDecimal TEN_THOUSAND = new BigDecimal("10000");

    /**
     * 默认数字，一千
     */
    private static final BigDecimal THOUSAND = new BigDecimal("1000");

    /**
     * 默认数字，一百
     */
    private static final BigDecimal HUNDRED = new BigDecimal("100");

    /**
     * 数组，对应下标（阿拉伯数字）的大写汉字
     */
    private static final StringBuffer CHINESE_NUMBER = new StringBuffer("零壹贰叁肆伍陆柒捌玖");

    /**
     * 做加法
     * @param cA 加数
     * @param cB 加数
     * @return 和
     */
    public static BigDecimal add(BigDecimal cA, BigDecimal cB) {
        return cA.add(cB);
    }

    /**
     * 做减法
     * @param cA 被减数
     * @param cB 减数
     * @return 差
     */
    public static BigDecimal sub(BigDecimal cA, BigDecimal cB) {
        return cA.subtract(cB);
    }

    /**
     * 做乘法
     * @param cA 乘数
     * @param cB 乘数
     * @return 积
     */
    public static BigDecimal mul(BigDecimal cA, BigDecimal cB) {
        return cA.multiply(cB);
    }

    /**
     * 做除法（有精度，结果四舍五入）
     * @param cA 被除数
     * @param cB 除数
     * @param cScale 精度
     * @return 商
     */
    public static BigDecimal divRound(BigDecimal cA, BigDecimal cB, int cScale) {
        if (cScale < 0) {
            cScale = DEF_DIV_SCALE;
        }
        return cA.divide(cB, cScale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 做除法（完整精度）
     * @param cA 被除数
     * @param cB 除数
     * @return 商
     */
    public static BigDecimal div(BigDecimal cA, BigDecimal cB) {
        return cA.divide(cB);
    }

    /**
     * 做除法（有精度，指定小数位之后，全部舍去）
     * @param cA 被除数
     * @param cB 除数
     * @param cScale 精度
     * @return 商
     */
    public static BigDecimal divFloor(BigDecimal cA, BigDecimal cB, int cScale) {
        if (cScale < 0) {
            cScale = DEF_DIV_SCALE;
        }
        return cA.divide(cB, cScale, BigDecimal.ROUND_FLOOR);
    }

    /**
     * 乘方运算
     * @param cA 底数
     * @param cB 指数
     * @return 计算结果
     */
    public static BigDecimal pow(BigDecimal cA, BigDecimal cB) {
        double tA = cA.doubleValue();
        double tB = cB.doubleValue();
        return new BigDecimal(String.valueOf(Math.pow(tA, tB)));
    }

    /**
     * 根据指定的小数位数，四舍五入
     * @param cA 原数字
     * @param cScale 小数位数
     * @return 处理后的数字
     */
    public static BigDecimal round(BigDecimal cA, int cScale) {
        if (cScale < 0) {
            cScale = DEF_DIV_SCALE;
        }
        BigDecimal tOne = new BigDecimal("1");
        return cA.divide(tOne, cScale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 根据指定的小数位数，舍去尾数
     * @param cA 原数字
     * @param cScale 小数位数
     * @return 处理后的数字
     */
    public static BigDecimal floor(BigDecimal cA, int cScale) {
        if (cScale < 0) {
            cScale = DEF_DIV_SCALE;
        }
        BigDecimal tOne = new BigDecimal("1");
        return cA.divide(tOne, cScale, BigDecimal.ROUND_FLOOR);
    }

    /**
     * 比较大小，a>b
     * @param cA 第一个参数
     * @param cB 第二个参数
     * @return boolean型结果，a>b则为true，反之为false
     */
    public static boolean larger(BigDecimal cA, BigDecimal cB) {
        boolean tResult = false;
        if (1 == cA.compareTo(cB)) {
            tResult = true;
        }
        return tResult;
    }

    /**
     * 比较大小，a<b
     * @param cA 第一个参数
     * @param cB 第二个参数
     * @return boolean型结果，a<b则为true，反之为false
     */
    public static boolean smaller(BigDecimal cA, BigDecimal cB) {
        boolean tResult = false;
        if (-1 == cA.compareTo(cB)) {
            tResult = true;
        }
        return tResult;
    }

    /**
     * 比较大小，a=b
     * @param cA 第一个参数
     * @param cB 第二个参数
     * @return boolean型结果，a=b则为true，反之为false
     */
    public static boolean equal(BigDecimal cA, BigDecimal cB) {
        boolean tResult = false;
        if (0 == cA.compareTo(cB)) {
            tResult = true;
        }
        return tResult;
    }

    /**
     * 比较大小，a>=b
     * @param cA 第一个参数
     * @param cB 第二个参数
     * @return boolean型结果，a>=b则为true，反之为false
     */
    public static boolean largerOrEqual(BigDecimal cA, BigDecimal cB) {
        boolean tResult = false;
        if (0 <= cA.compareTo(cB)) {
            tResult = true;
        }
        return tResult;
    }

    /**
     * 比较大小，a<=b
     * @param cA 第一个参数
     * @param cB 第二个参数
     * @return boolean型结果，a<=b则为true，反之为false
     */
    public static boolean smallerOrEqual(BigDecimal cA, BigDecimal cB) {
        boolean tResult = false;
        if (0 >= cA.compareTo(cB)) {
            tResult = true;
        }
        return tResult;
    }

    /**
     * 根据阿拉伯数字，取得中文大写汉字
     * @param cValue 一位的阿拉伯数字
     * @return 大写汉字
     */
    private static char getChineseNum(int cValue) {
        return CHINESE_NUMBER.charAt(cValue);
    }

    /**
     * 将小数部分，转化成大写汉字
     * @param cOriginValue 阿拉伯数字金额的小数部分
     * @return 大写汉字表示的金额
     */
    private static StringBuffer formtDecimalToChinese(BigDecimal cOriginValue) {
        // 返回结果，小数部分
        StringBuffer tResult = new StringBuffer();
        int tValue = round(mul(cOriginValue, HUNDRED), 0).intValue();
        if (10 <= tValue) {
            int tTenValue = tValue / 10;
            tValue = tValue % 10;
            tResult.append(getChineseNum(tTenValue));
            tResult.append("角");
        } else {
            tResult.append("零");
        }
        if (0 < tValue) {
            tResult.append(getChineseNum(tValue));
            tResult.append("分");
        }
        return tResult;
    }

    /**
     * 将整数部分表示成大写汉字
     * @param cOriginValue 金额的整数部分
     * @return 大写汉字表示的金额
     */
    private static StringBuffer formtIntegerToChinese(BigDecimal cOriginValue) {
        // 返回结果，整数部分
        StringBuffer tResult = new StringBuffer();
        // 当前数
        BigDecimal tCurValue = new BigDecimal(cOriginValue.toString());
        // 判断金额大于一亿
        if (0 <= tCurValue.compareTo(HUNDRED_MILLION)) {
            BigDecimal tHundredMillionValue = divFloor(tCurValue, HUNDRED_MILLION, 0);
            tCurValue = sub(tCurValue, mul(tHundredMillionValue, HUNDRED_MILLION));
            tResult.append(formatThousandsValueToChinese(tHundredMillionValue));
            tResult.append("亿");
        }
        // 金额大于一万
        if (0 <= tCurValue.compareTo(TEN_THOUSAND)) {
            // 
            BigDecimal tTenThousandValue = divFloor(tCurValue, TEN_THOUSAND, 0);
            tCurValue = sub(tCurValue, mul(tTenThousandValue, TEN_THOUSAND));
            if (0 > tTenThousandValue.compareTo(THOUSAND)) {
                tResult.append("零");
            }
            tResult.append(formatThousandsValueToChinese(tTenThousandValue));
            tResult.append("万");
        }
        // 金额的万以下部分
        if (0 <= tCurValue.compareTo(BigDecimal.ZERO)) {
            if (0 > tCurValue.compareTo(THOUSAND)) {
                tResult.append("零");
            }
            tResult.append(formatThousandsValueToChinese(tCurValue));
        }
        if (0 != tResult.length()) {
            tResult.append("元");
        }
        return tResult;
    }

    /**
     * 将千位以内的数字，转化成大写汉字
     * @param cOriginValue 阿拉伯数字
     * @return 大写汉字
     */
    private static StringBuffer formatThousandsValueToChinese(BigDecimal cOriginValue) {
        // 返回结果千位级别汉字表示数字
        StringBuffer tResult = new StringBuffer();
        // “零”状态位
        boolean tZeroFlag = false;
        BigDecimal tCurValue = new BigDecimal(cOriginValue.toString());
        // 金额大于一千
        if (0 <= tCurValue.compareTo(THOUSAND)) {
            BigDecimal tThousandValue = divFloor(tCurValue, THOUSAND, 0);
            tCurValue = sub(tCurValue, mul(tThousandValue, THOUSAND));
            tResult.append(getChineseNum(tThousandValue.intValue()));
            tResult.append("仟");
        } else if (!tZeroFlag && 0 != tResult.length()) {
            tZeroFlag = true;
            tResult.append("零");
        }
        // 金额大于一百
        if (0 <= tCurValue.compareTo(HUNDRED)) {
            BigDecimal tHundredValue = divFloor(tCurValue, HUNDRED, 0);
            tCurValue = sub(tCurValue, mul(tHundredValue, HUNDRED));
            tResult.append(getChineseNum(tHundredValue.intValue()));
            tResult.append("百");
        } else if (!tZeroFlag && 0 != tResult.length()) {
            tZeroFlag = true;
            tResult.append("零");
        }
        // 金额大于十
        if (0 <= tCurValue.compareTo(BigDecimal.TEN)) {
            BigDecimal tTenValue = divFloor(tCurValue, BigDecimal.TEN, 0);
            tCurValue = sub(tCurValue, mul(tTenValue, BigDecimal.TEN));
            tResult.append(getChineseNum(tTenValue.intValue()));
            tResult.append("拾");
        } else if (!tZeroFlag && 0 != tResult.length()) {
            tZeroFlag = true;
            tResult.append("零");
        }
        // 金额的个位部分
        if (0 < tCurValue.compareTo(BigDecimal.ZERO)) {
            tResult.append(getChineseNum(tCurValue.intValue()));
        }
        return tResult;
    }

    /**
     * 将阿拉伯数字的金额，转换成大写汉字（分单位以下，四舍五入）
     * @param cAmount 阿拉伯数字表示的金额
     * @return 大写汉字表示的金额
     */
    public static StringBuffer transAmountToChinese(BigDecimal cAmount) {
        StringBuffer tChineseAmount = new StringBuffer();
        int tLevel = cAmount.compareTo(BigDecimal.ZERO);
        switch (tLevel) {
        case -1:
            tChineseAmount.append("负");
        break;
        case 0:
            tChineseAmount.append("零元整");
        break;
        case 1:
            BigDecimal tIntegerPart = round(cAmount, 0);
            BigDecimal tDecimalPart = sub(cAmount, tIntegerPart);
            if (tIntegerPart.compareTo(BigDecimal.ZERO) >= 0) {
                StringBuffer tIntegerString = formtIntegerToChinese(tIntegerPart);
                tChineseAmount.append(tIntegerString);
            }
            StringBuffer tDecimalString = formtDecimalToChinese(tDecimalPart);
            tChineseAmount.append(tDecimalString);
            int tDecimalLength = tDecimalString.length();
            switch (tDecimalLength) {
            case 0:
                tChineseAmount.append("整");
            break;
            case 2:
                if ('角' == tDecimalString.charAt(1)) {
                    tChineseAmount.append("整");
                }
            break;
            }
        break;
        }
        return tChineseAmount;
    }

    /**
     * 保留两位小数位数格式化显示金额
     * @param cAmount 金额
     * @return 格式化后的金额
     */
    public static String formatAmount(BigDecimal cAmount) {
        if (cAmount == null || cAmount.doubleValue() == 0) {
            return "0.00";
        }
        DecimalFormat tSdf = new DecimalFormat("###,##0.00");
        return tSdf.format(cAmount);
    }

    /**
     * 保留两位小数位数格式化显示金额
     * @param cAmount 金额
     * @return 格式化后的金额
     */
    public static String formatAmount2(BigDecimal cAmount) {
        if (cAmount == null || cAmount.doubleValue() == 0) {
            return "0.00";
        }
        DecimalFormat tSdf = new DecimalFormat("##0.00");
        return tSdf.format(cAmount);
    }

    /**
     * 根据需要保留的小数位数格式化显示金额
     * @param cAmount 金额
     * @param cCount 小数位数
     * @return 格式化后的金额
     */
    public static String formatAmountByRound(BigDecimal cAmount, int cCount) {
        if (cAmount == null || cAmount.doubleValue() == 0) {
            cAmount = new BigDecimal(0);
        }
        String tFormatStr = "##0";

        if (cCount > 0) {
            tFormatStr = "##0.";
            for (int i = 0; i < cCount; i++) {
                tFormatStr += "0";
            }
        }
        DecimalFormat tSdf = new DecimalFormat(tFormatStr);
        return tSdf.format(cAmount);
    }

    /**
     * 根据需要保留的小数位数格式化显示金额
     * @param cAmount 金额
     * @param cCount 小数位数
     * @return 格式化后的金额
     */
    public static String formatAmountByRound2(BigDecimal cAmount, int cCount) {
        if (cAmount == null || cAmount.doubleValue() == 0) {
            cAmount = new BigDecimal(0);
        }
        String tFormatStr = "###,##0";

        if (cCount > 0) {
            tFormatStr = "###,##0.";
            for (int i = 0; i < cCount; i++) {
                tFormatStr += "0";
            }
        }
        DecimalFormat tSdf = new DecimalFormat(tFormatStr);
        return tSdf.format(cAmount);
    }

    /**
     * 格式化显示百分比
     * @param cAmount 百分比
     * @return 格式化后的百分比
     */
    public static String formatPercent(BigDecimal cAmount) {
        if (cAmount == null || cAmount.doubleValue() == 0) {
            return "0.00%";
        }
        DecimalFormat tFormat = new DecimalFormat("#0.00%");
        return tFormat.format(cAmount);
    }

    /**
     * 格式化显示汇率
     * @param cRate 汇率值
     * @return 格式化后的汇率值
     */
    public static String formatRate(Float cRate) {
        if (cRate == null || cRate.floatValue() == 0) {
            return "0.00";
        }
        DecimalFormat tSdf = new DecimalFormat("####.00");
        return tSdf.format(cRate);
    }

    /**
     * 将百分数转换成小数
     * @param cValue 要操作的字符串
     * @return String 处理后的小数值
     */
    public static double changePercentToDecimal(String cValue) {
        String tPercent = cValue.substring(0, cValue.lastIndexOf("%"));
        double tP = Double.parseDouble(tPercent);
        return tP / 100;
    }
    
    /***********************以下方法整合自 DecimalUtil工具类******************************************/
    /**
     * 
     * 保留小数点后的几位小数的方法（四舍五入）
     * @param cDist double类型浮点数
     * @param cD 需要保留的小数位数
     * @return 处理后的double浮点数
     */
    public static double subDecimalDigits(double cDist, int cD) {
        NumberFormat tDdf = NumberFormat.getNumberInstance();
        tDdf.setMaximumFractionDigits(cD);
        String tResult = tDdf.format(cDist);
        // 将数值中的逗号去掉
        tResult = tResult.replaceAll(",", "");
        return Double.valueOf(tResult);
    }

    /**
     * 传入List<BigDecimal>数据，返回每个list中每个数的所占总数的比例。简单demo说明<br/>
     * <pre>
     *  BigDecimal big1 = new BigDecimal("2.2");
     *  BigDecimal big2 = new BigDecimal("3.3");
     *  List<BigDecimal> list = new ArrayList<BigDecimal>();
     *  list.add(big1);
     *  list.add(big2);
     *  double[] ds = calcPercentage(list);
     *  输出结果：[0.4, 0.6] 
     * </pre>
     *  计算过程：<br/>
     *  <pre>
     *  sum = big1+big2
     *  d1 = big1/sum; //d1最多只保留4位小数
     *  d2 = big2/sum;//d2最多只保留4位小数
     *  ds[]={d1,d2}
     *  </pre>
     * @param cShares List<BigDecimal> 数组
     * @return List<BigDecimal>数组中每个数所占的比例的数组，并且数组中的double保留4位小数
     */
    public static double[] calcPercentage(List<BigDecimal> cShares) {
        BigDecimal tSum = new BigDecimal("0");
        for (BigDecimal tShare : cShares) {
            tSum = tSum.add(tShare);
        }
        double[] tArr = new double[cShares.size()];
        for (int i = 0; i < cShares.size(); i++) {
            double tPercentage = cShares.get(i).divide(tSum, 10, RoundingMode.HALF_DOWN).doubleValue();
            tArr[i] = subDecimalDigits(tPercentage, 4);
        }
        return tArr;
    }

    /**
     * 
     * 方法功能描述
     * @param cList
     * @param cFieldName
     * @return
     * @throws Exception
     */
    @SuppressWarnings("rawtypes")
    public static BigDecimal sumByField(List cList, String cFieldName) throws Exception {
        BigDecimal tSum = new BigDecimal("0");
        for (Object tObj : cList) {
            Object tValue = ClassInvokeUtil.getMethodValue(tObj, cFieldName);
            if (tValue instanceof BigDecimal) {
                tSum = tSum.add((BigDecimal) tValue);
            } else {
                throw new RuntimeException(cList + "中元素属性" + cFieldName + "不支持非Decimal类型的求和操作");
            }
        }
        return tSum;
    }
    
    /**
     * 
     * 方法功能描述
     * @param cList
     * @param cFieldName
     * @param cGroupBy
     * @return
     * @throws Exception
     */
    @SuppressWarnings("rawtypes")
    public static Map<Object, BigDecimal> sumByField(List cList, String cFieldName, String cGroupBy) throws Exception {
        Map<Object, BigDecimal> tResultMap = new HashMap<Object, BigDecimal>();
        for (Object tObj : cList) {
            Object tGroupVal = ClassInvokeUtil.getMethodValue(tObj, cGroupBy);
            Object tValue = ClassInvokeUtil.getMethodValue(tObj, cFieldName);
            if (tResultMap.containsKey(tGroupVal)) {
                tResultMap.put(tGroupVal, tResultMap.get(tGroupVal).add((BigDecimal) tValue));
            } else {
                tResultMap.put(tGroupVal, (BigDecimal) tValue);
            }
        }
        return tResultMap;
    }
    
    /**
     * 
     * 方法功能描述
     * @Title: sumByFieldWithFilter 
     * @Description: 根据多重的过滤条件对List中的指定字段求和
     * @param cList
     * @param cFieldName
     * @param cFilterMap
     * @return
     * @throws Exception
     */
    @SuppressWarnings("rawtypes")
    public static BigDecimal sumByFieldWithFilter(List cList, String cFieldName, Map<String, Object> cFilterMap) throws Exception {
        BigDecimal tSum = new BigDecimal("0");
        for (Object tObj : cList) {
            Object tValue = ClassInvokeUtil.getMethodValue(tObj, cFieldName);
            boolean tMatched = true;
            for (Map.Entry<String, Object> tEntry : cFilterMap.entrySet()) {
                String tField = tEntry.getKey();
                Object tMappingValue = tEntry.getValue();
                Object tActualValue = ClassInvokeUtil.getMethodValue(tObj, tField);
                if (!tActualValue.equals(tMappingValue)) {
                    tMatched = false;
                    break;
                }
            }
            if (tMatched) {
                if (tValue instanceof BigDecimal) {
                    tSum = tSum.add((BigDecimal) tValue);
                } else {
                    throw new RuntimeException(cList + "中元素属性" + cFieldName + "不支持非Decimal类型的求和操作");
                }
            }
        }
        return tSum;
    }

  

}
